package cn.concurrent.finaL;

/** 写final域重排序规则
 *  读final域重排序
 * @Author: lz
 * @Date: 2018/8/22 14:08
 * @Version 1.0
 */
public class FinalDemo {
	private int a;  //普通域
	private final int b; //final域
	private static FinalDemo finalDemo;

	public FinalDemo() {
		a = 1; // 1. 写普通域
		b = 2; // 2. 写final域
	}

	public static void writer() {
		finalDemo = new FinalDemo();
	}

	public static void reader() {
		FinalDemo demo = finalDemo; // 3.读对象引用
		int a = demo.a;    //4.读普通域
		int b = demo.b;    //5.读final域

		/**
		 * 经过多次两个线程操作，读写第24行时不时会报错.NullPointerException
		 *
		 * 写final域的重排序规则禁止对final域的写重排序到构造函数之外（storestore）
		 * 普通域（普通变量）a可能会被重排序到构造函数之外，线程B就有可能读到的是普通变量a初始化之前的值（零值），这样就可能出现错误。
		 * 而final域变量b，根据重排序规则，会禁止final修饰的变量b重排序到构造函数之外，从而b能够正确赋值，线程B就能够读到final变量初始化后的值。
		 * 在对象引用为任意线程可见之前，对象的final域已经被正确初始化过了，而普通域就不具有这个保障。比如在上例，线程B有可能就是一个未正确初始化的对象finalDemo。
		 *
		 * 假设写的线程没有重排序
		 *
		 * read()方法主要包含了三个操作：
		 * 初次读引用变量finalDemo;
		 * 初次读引用变量finalDemo的普通域a;
		 * 初次读引用变量finalDemo的final与b;
		 * 没有重排序可能为普通域a > finalDemo对象 > final域b
		 * 读对象的普通域被重排序到了读对象引用的前面就会出现线程B还未读到对象引用就在读取该对象的普通域变量，这显然是错误的操作。
		 * 而final域的读操作就“限定”了在读final域变量前已经读到了该对象的引用，从而就可以避免这种情况
		 *
		 */

	}

}

